package com.tianhai.db.index;

import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Random;

import com.tianhai.db.DiskManager;

/**
 * B+树性能测试类
 * 测试100万随机主键的插入和点查平均页面访问次数
 */
public class BPlusTreePerformanceTest {
    
    // 测试参数
    private static final int TOTAL_KEYS = 1_000_000;  // 100万条记录
    private static final int SAMPLE_SIZE = 10_000;     // 采样查询数量
    private static final int ORDER = 128;              // B+树阶数，更大的阶数减少树高
    private static final double MAX_AVG_PAGE_ACCESS = 4.0;  // 最大平均页面访问次数
    
    // 页面访问计数器
    private static ThreadLocal<Integer> pageAccessCounter = ThreadLocal.withInitial(() -> 0);
    
    public static void main(String[] args) {
        System.out.println("========================================");
        System.out.println("    B+树性能测试 - 100万随机主键");
        System.out.println("========================================\n");
        
        DiskManager diskManager = null;
        String dbFilePath = "bplustree_perf_test.db";
        
        try {
            // 清理旧的测试文件
            File oldFile = new File(dbFilePath);
            if (oldFile.exists()) {
                oldFile.delete();
                System.out.println("✓ 已清理旧测试文件\n");
            }
            
            // 初始化
            System.out.println("【初始化】");
            diskManager = new InstrumentedDiskManager(dbFilePath);
            System.out.println("✓ DiskManager创建成功");
            System.out.println("  - 数据库文件: " + dbFilePath);
            System.out.println();
            
            // 测试配置
            System.out.println("【测试配置】");
            System.out.println("  - 主键数量: " + formatNumber(TOTAL_KEYS) + " 个");
            System.out.println("  - B+树阶数: " + ORDER);
            System.out.println("  - 采样查询数量: " + formatNumber(SAMPLE_SIZE) + " 个");
            System.out.println("  - 性能要求: 平均页面访问次数 < " + MAX_AVG_PAGE_ACCESS + " 次");
            System.out.println();
            
            // 生成随机主键
            System.out.println("【生成随机主键】");
            long genStart = System.currentTimeMillis();
            List<Integer> keys = generateRandomKeys(TOTAL_KEYS);
            long genEnd = System.currentTimeMillis();
            System.out.println("✓ 随机主键生成完成");
            System.out.println("  - 生成耗时: " + (genEnd - genStart) + " ms");
            System.out.println();
            
            // 创建B+树并插入数据
            System.out.println("【插入测试】");
            System.out.println("正在插入 " + formatNumber(TOTAL_KEYS) + " 个主键...");
            
            BPlusTree bTree = new BPlusTree(diskManager, ORDER);
            long insertStart = System.currentTimeMillis();
            
            for (int i = 0; i < keys.size(); i++) {
                int key = keys.get(i);
                bTree.insert(key, key * 10);  // value = key * 10
                
                // 进度显示（每1万条显示一次）
                if ((i + 1) % 10000 == 0) {
                    double progress = (i + 1) * 100.0 / keys.size();
                    long elapsed = System.currentTimeMillis() - insertStart;
                    long eta = (long) (elapsed / (i + 1.0) * (keys.size() - i - 1));
                    System.out.print(String.format("  进度: %s / %s (%.1f%%) - 已用时: %s - 预计剩余: %s\r",
                        formatNumber(i + 1), formatNumber(keys.size()), progress,
                        formatTime(elapsed), formatTime(eta)));
                }
            }
            
            long insertEnd = System.currentTimeMillis();
            long insertTimeMs = insertEnd - insertStart;
            
            System.out.println("\n✓ 插入测试完成\n");
            
            // 插入性能统计
            System.out.println("【插入性能统计】");
            System.out.println("  - 总耗时: " + formatTime(insertTimeMs));
            System.out.println("  - 平均每个主键耗时: " + 
                String.format("%.4f", (double) insertTimeMs / TOTAL_KEYS) + " ms");
            System.out.println("  - 吞吐量: " + 
                String.format("%.0f", TOTAL_KEYS * 1000.0 / insertTimeMs) + " 个/秒");
            
            // 检查单操作耗时
            double avgInsertTimeMs = (double) insertTimeMs / TOTAL_KEYS;
            boolean insertPerfPassed = avgInsertTimeMs < 5.0;
            System.out.println("\n  插入性能检查: " + (insertPerfPassed ? "✓ 通过" : "✗ 失败"));
            if (!insertPerfPassed) {
                System.out.println("    实际: " + String.format("%.4f", avgInsertTimeMs) + 
                    " ms > 要求: 5.0 ms");
            } else {
                System.out.println("    实际: " + String.format("%.4f", avgInsertTimeMs) + 
                    " ms < 要求: 5.0 ms ✓");
            }
            System.out.println();
            
            // 刷新到磁盘
            System.out.println("【刷新到磁盘】");
            long flushStart = System.currentTimeMillis();
            diskManager.flush();
            long flushEnd = System.currentTimeMillis();
            System.out.println("✓ 数据已刷新到磁盘");
            System.out.println("  - 刷新耗时: " + (flushEnd - flushStart) + " ms");
            System.out.println();
            
            // 点查性能测试
            System.out.println("【点查性能测试】");
            System.out.println("正在随机查询 " + formatNumber(SAMPLE_SIZE) + " 个主键...");
            
            // 随机采样查询的键
            List<Integer> sampleKeys = new ArrayList<>();
            Random random = new Random(54321);
            for (int i = 0; i < SAMPLE_SIZE; i++) {
                sampleKeys.add(keys.get(random.nextInt(keys.size())));
            }
            
            // 重置页面访问计数
            long totalPageAccess = 0;
            long queryStart = System.currentTimeMillis();
            int successCount = 0;
            int failCount = 0;
            
            for (int i = 0; i < sampleKeys.size(); i++) {
                int key = sampleKeys.get(i);
                
                try {
                    // 重置当前查询的页面访问计数
                    pageAccessCounter.set(0);
                    
                    // 执行查询
                    Integer result = bTree.search(key);
                    
                    // 验证结果
                    if (result != null && result == key * 10) {
                        successCount++;
                        totalPageAccess += pageAccessCounter.get();
                    }
                } catch (Exception e) {
                    // 忽略错误，继续测试
                    failCount++;
                }
                
                // 进度显示（每1000条显示一次）
                if ((i + 1) % 1000 == 0) {
                    double progress = (i + 1) * 100.0 / sampleKeys.size();
                    System.out.print(String.format("  查询进度: %s / %s (%.1f%%)\r",
                        formatNumber(i + 1), formatNumber(sampleKeys.size()), progress));
                }
            }
            
            long queryEnd = System.currentTimeMillis();
            long queryTimeMs = queryEnd - queryStart;
            
            System.out.println("\n✓ 点查测试完成\n");
            
            // 点查性能统计
            System.out.println("【点查性能统计】");
            System.out.println("  - 查询总数: " + formatNumber(sampleKeys.size()));
            System.out.println("  - 查询成功: " + formatNumber(successCount) + " (" +
                String.format("%.2f%%", successCount * 100.0 / sampleKeys.size()) + ")");
            System.out.println("  - 总耗时: " + queryTimeMs + " ms");
            System.out.println("  - 平均每次查询耗时: " + 
                String.format("%.4f", (double) queryTimeMs / sampleKeys.size()) + " ms");
            System.out.println("  - 查询吞吐量: " + 
                String.format("%.0f", sampleKeys.size() * 1000.0 / queryTimeMs) + " 个/秒");
            
            // 计算平均页面访问次数
            double avgPageAccess = (double) totalPageAccess / successCount;
            System.out.println("\n  - 总页面访问次数: " + formatNumber(totalPageAccess));
            System.out.println("  - 平均页面访问次数: " + String.format("%.4f", avgPageAccess) + " 次");
            
            // 性能要求检查
            boolean pageAccessPassed = avgPageAccess < MAX_AVG_PAGE_ACCESS;
            System.out.println("\n  页面访问性能检查: " + (pageAccessPassed ? "✓ 通过" : "✗ 失败"));
            if (!pageAccessPassed) {
                System.out.println("    实际: " + String.format("%.4f", avgPageAccess) + 
                    " 次 > 要求: " + MAX_AVG_PAGE_ACCESS + " 次");
            } else {
                System.out.println("    实际: " + String.format("%.4f", avgPageAccess) + 
                    " 次 < 要求: " + MAX_AVG_PAGE_ACCESS + " 次 ✓");
            }
            System.out.println();
            
            // B+树结构信息
            System.out.println("【B+树结构信息】");
            int totalPages = diskManager.getTotalPages();
            System.out.println("  - 总页面数: " + formatNumber(totalPages));
            System.out.println("  - 文件大小: " + formatBytes(new File(dbFilePath).length()));
            System.out.println("  - 理论树高度: " + calculateTreeHeight(TOTAL_KEYS, ORDER));
            System.out.println();
            
            // 最终测试结果
            System.out.println("========================================");
            System.out.println("           测试结果汇总");
            System.out.println("========================================");
            
            boolean allPassed = insertPerfPassed && pageAccessPassed && (successCount == sampleKeys.size());
            
            System.out.println("插入性能测试: " + (insertPerfPassed ? "✓ 通过" : "✗ 失败"));
            System.out.println("点查成功率测试: " + ((successCount == sampleKeys.size()) ? "✓ 通过" : "✗ 失败"));
            System.out.println("页面访问性能测试: " + (pageAccessPassed ? "✓ 通过" : "✗ 失败"));
            System.out.println("----------------------------------------");
            
            if (allPassed) {
                System.out.println("✓✓✓ 所有性能测试通过！");
            } else {
                System.out.println("✗✗✗ 部分性能测试失败");
            }
            
            System.out.println("========================================\n");
            
        } catch (Exception e) {
            System.err.println("✗ 测试过程中发生异常: " + e.getMessage());
            e.printStackTrace();
        } finally {
            if (diskManager != null && !diskManager.isClosed()) {
                try {
                    diskManager.close();
                } catch (IOException e) {
                    System.err.println("✗ 关闭DiskManager时发生异常: " + e.getMessage());
                }
            }
        }
    }
    
    /**
     * 生成随机主键
     */
    private static List<Integer> generateRandomKeys(int count) {
        List<Integer> keys = new ArrayList<>(count);
        for (int i = 0; i < count; i++) {
            keys.add(i);
        }
        
        // 打乱顺序，生成随机序列
        Collections.shuffle(keys, new Random(12345));
        
        return keys;
    }
    
    /**
     * 计算理论树高度
     */
    private static int calculateTreeHeight(int totalKeys, int order) {
        if (totalKeys <= 0) return 0;
        
        // 叶子节点最多存储 order-1 个键
        int maxKeysPerLeaf = order - 1;
        
        // 计算需要的叶子节点数
        int leafNodes = (int) Math.ceil((double) totalKeys / maxKeysPerLeaf);
        
        // 内部节点最多有 order 个子节点
        int height = 1;
        int currentLevelNodes = leafNodes;
        
        while (currentLevelNodes > 1) {
            currentLevelNodes = (int) Math.ceil((double) currentLevelNodes / order);
            height++;
        }
        
        return height;
    }
    
    /**
     * 格式化数字（添加千位分隔符）
     */
    private static String formatNumber(int number) {
        return String.format("%,d", number);
    }
    
    /**
     * 格式化数字（添加千位分隔符）- long版本
     */
    private static String formatNumber(long number) {
        return String.format("%,d", number);
    }
    
    /**
     * 格式化时间
     */
    private static String formatTime(long ms) {
        if (ms < 1000) {
            return ms + " ms";
        } else if (ms < 60000) {
            return String.format("%.2f 秒", ms / 1000.0);
        } else {
            long minutes = ms / 60000;
            long seconds = (ms % 60000) / 1000;
            return String.format("%d 分 %d 秒", minutes, seconds);
        }
    }
    
    /**
     * 格式化字节大小
     */
    private static String formatBytes(long bytes) {
        if (bytes < 1024) {
            return bytes + " B";
        } else if (bytes < 1024 * 1024) {
            return String.format("%.2f KB", bytes / 1024.0);
        } else if (bytes < 1024 * 1024 * 1024) {
            return String.format("%.2f MB", bytes / (1024.0 * 1024.0));
        } else {
            return String.format("%.2f GB", bytes / (1024.0 * 1024.0 * 1024.0));
        }
    }
    
    /**
     * 带页面访问计数功能的DiskManager
     */
    private static class InstrumentedDiskManager extends DiskManager {
        
        public InstrumentedDiskManager(String dbFilePath) throws IOException {
            super(dbFilePath);
        }
        
        @Override
        public byte[] rawReadPage(int pageId) throws IOException {
            // 增加页面访问计数
            Integer count = pageAccessCounter.get();
            pageAccessCounter.set(count + 1);
            
            return super.rawReadPage(pageId);
        }
    }
}
